﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Assist;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Controls;
using YJKArchUtils.Utils;
using YJKElementFinish.ColumnSurface.DataDefinition;
using YJKElementFinish.ColumnSurface.Managers;
using YJKElementFinish.MultiWallEdit.Forms;
using YJKRevitPreview;
using YJKGeometryFuncs.CalculationAnalysis;

namespace YJKElementFinish.MultiWallEdit.Controller
{
	public class WallMergeController
	{
		public WallMergeController(ExternalCommandData cmdData)
		{
			this.m_ComdData = cmdData;
			this.m_UIApp = cmdData.Application;
			this.m_UIDoc = this.m_UIApp.ActiveUIDocument;
			this.m_App = this.m_UIApp.Application;
			this.m_Doc = this.m_UIDoc.Document;
			this.m_doffset = this.m_doffset / 100.0 * (double)this.m_UIDoc.ActiveView.Scale;
		}

		public Result Merge()
		{
			List<Wall> selWalls = new List<Wall>();
			if (!WallCommon.SelectWalls(this.m_UIDoc, ref selWalls))
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			WallGroupingController wallGroupingController = new WallGroupingController(this.m_ComdData);
			List<List<Wall>> list = new List<List<Wall>>();
			List<List<Wall>> list2 = new List<List<Wall>>();
			List<Wall> list3 = new List<Wall>();
			wallGroupingController.WallGrouping(selWalls, ref list, ref list2, ref list3);
			List<List<Wall>> list4 = new List<List<Wall>>();
			list4.AddRange(list);
			list4.AddRange(list2);
			List<List<WallType>> list5 = new List<List<WallType>>();
			List<KeyValuePair<int, bool>> list6 = new List<KeyValuePair<int, bool>>();
			wallGroupingController.GetWallTypeGroup(list4, ref list5, ref list6);
			if (list5.Count == 0)
			{
				RevitVersionFuncs.ClearSelection(this.m_UIDoc.Selection);
				YJKMessageBox.Show("无可合并的成组多墙");
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			WallTypeMergeForm wallTypeMergeForm = new WallTypeMergeForm(list5, this.m_Doc);
			if (wallTypeMergeForm.ShowDialog() != DialogResult.OK)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			List<List<List<WallTypeLayerInfos>>> combinationInfosByGroup = wallTypeMergeForm.GetCombinationInfosByGroup();
			List<List<List<WallTypeLayerInfos>>> list7 = new List<List<List<WallTypeLayerInfos>>>();
			int i;
			for (i = 0; i < list.Count; i++)
			{
				list7.Add(combinationInfosByGroup[list6[i].Key]);
			}
			List<List<List<WallTypeLayerInfos>>> list8 = new List<List<List<WallTypeLayerInfos>>>();
			while (i < list.Count + list2.Count)
			{
				list8.Add(combinationInfosByGroup[list6[i].Key]);
				i++;
			}
			List<Wall> lstNewWalls = new List<Wall>();
			this.MergeLinearWall(list, list7, ref lstNewWalls);
			this.MergeArcWall(list2, list8, ref lstNewWalls);
			List<KeyValuePair<FamilyInstance, XYZ>> newTagUsePoint = this.GetNewTagUsePoint(lstNewWalls);
			Transaction transaction = new Transaction(this.m_Doc, "InsertTag");
			try
			{
				transaction.Start();
				foreach (KeyValuePair<FamilyInstance, XYZ> keyValuePair in newTagUsePoint)
				{
					RevitVersionFuncs.NewTag(this.m_Doc, this.m_Doc.ActiveView, new Reference(keyValuePair.Key), false, 0, 0, keyValuePair.Value);
				}
				transaction.Commit();
			}
			catch (Exception)
			{
				transaction.RollBack();
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private List<KeyValuePair<FamilyInstance, XYZ>> GetNewTagUsePoint(List<Wall> lstNewWalls)
		{
			List<KeyValuePair<FamilyInstance, XYZ>> list = new List<KeyValuePair<FamilyInstance, XYZ>>();
			foreach (Wall wall in lstNewWalls)
			{
				if (wall != null)
				{
					IList<ElementId> list2 = null;
					try
					{
						list2 = wall.FindInserts(true, true, true, true);
					}
					catch (Exception)
					{
						continue;
					}
					if (list2.Count != 0)
					{
						foreach (ElementId elementId in list2)
						{
							Element element = this.m_Doc.GetElement(elementId);
							if (!(element.GetType() != typeof(FamilyInstance)))
							{
								FamilyInstance familyInstance = element as FamilyInstance;
								if (familyInstance.Host.Id.IntegerValue == wall.Id.IntegerValue)
								{
									XYZ point = ((LocationPoint)familyInstance.Location).Point;
									double width = wall.Width;
									XYZ value = XYZ.Zero;
									if (element.Category.Id.IntegerValue == -2000014)
									{
										value = point + familyInstance.FacingOrientation * (this.GetTagHeightOffset(familyInstance.FacingOrientation, this.m_UIDoc.ActiveView, false) + this.m_doffset + width);
									}
									else if (element.Category.Id.IntegerValue == -2000023)
									{
										double num = WallMergeController.MinAreaBoundingBoxPoint(this.m_UIDoc, familyInstance.Symbol);
										if (Geometry.LessThan(num, 1.0))
										{
											value = point - familyInstance.FacingOrientation * (this.GetTagHeightOffset(-familyInstance.FacingOrientation, this.m_UIDoc.ActiveView, true) + width + this.m_doffset + num * 0.5);
										}
										else
										{
											value = point - familyInstance.FacingOrientation * (this.GetTagHeightOffset(-familyInstance.FacingOrientation, this.m_UIDoc.ActiveView, true) + num * 0.5);
										}
									}
									list.Add(new KeyValuePair<FamilyInstance, XYZ>(familyInstance, value));
								}
							}
						}
					}
				}
			}
			return list;
		}

		private static double MinAreaBoundingBoxPoint(UIDocument m_uiDoc, FamilySymbol familySymbol)
		{
			double num = 0.0;
			double num2 = 0.0;
			double num3 = 0.0;
			List<XYZ> list = new List<XYZ>();
			Document document = m_uiDoc.Document.EditFamily(familySymbol.Family);
			UIApplication application = m_uiDoc.Application;
			Transaction transaction = new Transaction(document, "Type");
			try
			{
				transaction.Start();
				FamilyManager familyManager = document.FamilyManager;
				FamilyType currentType = familyManager.CurrentType;
				FamilyType familyType = null;
				foreach (object obj in familyManager.Types)
				{
					if (obj is FamilyType && (obj as FamilyType).Name.Equals(familySymbol.Name))
					{
						familyType = (obj as FamilyType);
						break;
					}
				}
				if (familyType != null)
				{
					familyManager.CurrentType = familyType;
				}
				Legend family2DImg = LegendCommon.GetFamily2DImg(application, document, LegendType.DoorWindows);
				familyManager.CurrentType = currentType;
				List<XYZ> list2 = new List<XYZ>();
				foreach (CurveInfo curveInfo in family2DImg.Curves)
				{
					Curve curveObj = curveInfo.CurveObj;
					list2.Add(curveObj.GetEndPoint(0));
					list2.Add(curveObj.GetEndPoint(1));
				}
				list2 = list2.Distinct(new XYZCompare()).ToList<XYZ>();
				MinAreaBoundingBoxCalculator.MinAreaRect(list2, ref list, ref num2, ref num3);
				foreach (XYZ xyz in list)
				{
					if (num < xyz.Y)
					{
						num = xyz.Y;
					}
				}
				transaction.Commit();
			}
			catch
			{
				transaction.RollBack();
			}
			return num;
		}

		private double GetTagHeightOffset(XYZ facingOrientation, Autodesk.Revit.DB.View view, bool isDoor)
		{
			double num = Common.MMToFeet(550.0);
			if (!isDoor)
			{
				num = Common.MMToFeet(350.0);
			}
			num = num / 100.0 * (double)view.Scale;
			double num2 = Geometry.getAngle(XYZ.Zero, facingOrientation);
			num2 = Geometry.formatAngle(num2 + Math.PI*.5);
			if (Geometry.GreaterThan(num2, Math.PI*.5) && Geometry.Lessthan_Or_Equal(num2, 4.71238898038469))
			{
				return -num;
			}
			return num;
		}

		private void MergeLinearWall(List<List<Wall>> linearWalls, List<List<List<WallTypeLayerInfos>>> combinationInfosByGroup, ref List<Wall> lstNewWalls)
		{
			for (int i = 0; i < linearWalls.Count; i++)
			{
				List<Wall> list = linearWalls[i];
				List<List<WallTypeLayerInfos>> list2 = combinationInfosByGroup[i];
				int j = 0;
				int num = 0;
				while (j < list2.Count)
				{
					List<WallTypeLayerInfos> list3 = list2[j];
					List<Wall> range = list.GetRange(num, list3.Count);
					Wall item = null;
					this.MergeSingleGroupWall(range, ref item);
					lstNewWalls.Add(item);
					num += list3.Count;
					j++;
				}
			}
		}

		private bool MergeSingleGroupWall(List<Wall> walls, ref Wall newWall)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_Doc);
				transaction.Start("merge wall");
				Wall wall = WallCommon.FindMainWall(walls, this.m_Doc);
				if (wall == null)
				{
					transaction.RollBack();
					return false;
				}
				bool hasFlip = this.HasFlipWallGroup(wall, ref walls);
				List<CompoundStructureLayer> layers = new List<CompoundStructureLayer>();
				double width = 0.0;
				List<ElementId> list = new List<ElementId>();
				this.GetMergeInfo(walls, hasFlip, ref list, ref layers, ref width);
				string typeNameByLayers = WallTypeManager.GetTypeNameByLayers(layers, this.m_Doc);
				WallUtils.DisallowWallJoinAtEnd(wall, 0);
				WallUtils.DisallowWallJoinAtEnd(wall, 1);
				WallType wallType = WallTypeManager.GetWallType(typeNameByLayers, wall.WallType, layers, this.m_Doc, false);
				XYZ xyz = XYZ.BasisX * 100.0 + XYZ.BasisY * 100.0;
				if (wallType != null)
				{
					ICollection<ElementId> source = ElementTransformUtils.CopyElement(this.m_Doc, wall.Id, xyz);
					newWall = (this.m_Doc.GetElement(source.ElementAt(0)) as Wall);
					if (newWall.Flipped)
					{
						newWall.Flip();
					}
					Curve curve = null;
					this.GetOffsetLocationWithMergedWall(wall, walls, width, xyz, ref curve);
					(newWall.Location as LocationCurve).Curve = curve;
					WallUtils.AllowWallJoinAtEnd(newWall, 0);
					WallUtils.AllowWallJoinAtEnd(newWall, 1);
				}
				transaction.Commit();
				transaction = new Transaction(this.m_Doc);
				transaction.Start("delete old walls");
				foreach (ElementId elementId in list)
				{
					Wall wall2 = this.m_Doc.GetElement(elementId) as Wall;
					if (wall2 != null)
					{
						WallUtils.DisallowWallJoinAtEnd(wall2, 0);
						WallUtils.DisallowWallJoinAtEnd(wall2, 1);
					}
				}
				this.m_Doc.Delete(list);
				newWall.WallType = wallType;
				ElementTransformUtils.MoveElement(this.m_Doc, newWall.Id, -xyz);
				transaction.Commit();
			}
			catch (Exception)
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
				return false;
			}
			return true;
		}

		private void GetOffsetLocationWithMergedWall(Wall kernelWall, List<Wall> walls, double width, XYZ vtMove, ref Curve newCenterLine)
		{
			Curve curve = (kernelWall.Location as LocationCurve).Curve;
			if (curve.GetType() == typeof(Arc))
			{
				this.GetOffsetLocationWithMergedArcWall(curve, walls, width, vtMove, ref newCenterLine);
				return;
			}
			this.GetOffsetLocationWithMergedLinearWall(curve, walls, width, vtMove, ref newCenterLine);
		}

		private void GetOffsetLocationWithMergedLinearWall(Curve kernelWallCenterCurve, List<Wall> walls, double width, XYZ vtMove, ref Curve newCenterLine)
		{
			XYZ endPoint = kernelWallCenterCurve.GetEndPoint(0);
			XYZ endPoint2 = kernelWallCenterCurve.GetEndPoint(1);
			XYZ xyz = null;
			double num = 0.0;
			for (int i = 0; i < walls.Count; i++)
			{
				Wall wall = walls[i];
				Curve curve = (wall.Location as LocationCurve).Curve;
				curve.GetEndPoint(0);
				XYZ xyz2 = Geometry.CalculateFootPoint((Line)curve, endPoint);
				double num2 = wall.Width / 2.0;
				double num3 = endPoint.DistanceTo(xyz2) + num2;
				if (Geometry.LessThan(num, num3))
				{
					num = num3;
					xyz = xyz2;
				}
			}
			XYZ xyz3 = (xyz - endPoint).Normalize();
			double num4 = width / 2.0;
			double num5 = num - num4;
			XYZ xyz4 = endPoint + xyz3 * num5;
			XYZ xyz5 = endPoint2 + xyz3 * num5;
			newCenterLine = Line.CreateBound(xyz4 + vtMove, xyz5 + vtMove);
		}

		private void GetMergeInfo(List<Wall> walls, bool hasFlip, ref List<ElementId> oldWallIds, ref List<CompoundStructureLayer> layers, ref double width)
		{
			foreach (Wall wall in walls)
			{
				List<CompoundStructureLayer> list = wall.WallType.GetCompoundStructure().GetLayers().ToList<CompoundStructureLayer>();
				if (wall.Flipped)
				{
					list.Reverse();
				}
				layers.AddRange(list);
				width += wall.Width;
				oldWallIds.Add(wall.Id);
				WallUtils.DisallowWallJoinAtEnd(wall, 0);
				WallUtils.DisallowWallJoinAtEnd(wall, 1);
			}
		}

		private bool HasFlipWallGroup(Wall kernelWall, ref List<Wall> walls)
		{
			Curve curve = (kernelWall.Location as LocationCurve).Curve;
			if (curve.GetType() == typeof(Arc))
			{
				return this.HasFlipArcWallGroup(curve, ref walls);
			}
			return this.HasFlipLinearWallGroup(curve, ref walls);
		}

		private bool HasFlipLinearWallGroup(Curve kernelWallCenterCurve, ref List<Wall> walls)
		{
			Line line = (walls[0].Location as LocationCurve).Curve as Line;
			XYZ endPoint = ((walls[walls.Count - 1].Location as LocationCurve).Curve as Line).GetEndPoint(0);
			XYZ xyz = Geometry.CalculateFootPoint(line, endPoint);
			XYZ pt = (endPoint - xyz).Normalize();
			XYZ pt2 = Geometry.RotateTo((kernelWallCenterCurve as Line).Direction, Math.PI*.5, XYZ.BasisZ);
			if (Geometry.IsEqual(pt, pt2))
			{
				walls.Reverse();
				return false;
			}
			return true;
		}

		private void MergeArcWall(List<List<Wall>> arcWalls, List<List<List<WallTypeLayerInfos>>> combinationInfosByGroup, ref List<Wall> lstNewWalls)
		{
			for (int i = 0; i < arcWalls.Count; i++)
			{
				List<Wall> list = arcWalls[i];
				List<List<WallTypeLayerInfos>> list2 = combinationInfosByGroup[i];
				int j = 0;
				int num = 0;
				while (j < list2.Count)
				{
					List<WallTypeLayerInfos> list3 = list2[j];
					List<Wall> range = list.GetRange(num, list3.Count);
					Wall wall = null;
					this.MergeSingleGroupWall(range, ref wall);
					num += list3.Count;
					j++;
				}
			}
		}

		private void GetOffsetLocationWithMergedArcWall(Curve kernelWallCenterCurve, List<Wall> walls, double width, XYZ vtMove, ref Curve newCenterLine)
		{
			Arc arc = kernelWallCenterCurve as Arc;
			XYZ endPoint = arc.GetEndPoint(0);
			XYZ endPoint2 = arc.GetEndPoint(1);
			XYZ center = arc.Center;
			XYZ xyz = Geometry.CalculatMidPoint(endPoint, endPoint2, center, arc.Normal);
			XYZ xyz2 = null;
			double num = 0.0;
			for (int i = 0; i < walls.Count; i++)
			{
				Wall wall = walls[i];
				Curve curve = (wall.Location as LocationCurve).Curve;
				curve.GetEndPoint(0);
				XYZ xyz3 = Geometry.CalculateFootPoint((Arc)curve, endPoint);
				double num2 = wall.Width / 2.0;
				double num3 = endPoint.DistanceTo(xyz3) + num2;
				if (Geometry.LessThan(num, num3))
				{
					num = num3;
					xyz2 = xyz3;
				}
			}
			double num4 = width / 2.0;
			double num5 = num - num4;
			XYZ xyz4 = endPoint - (endPoint - center).Normalize() * num5;
			XYZ xyz5 = endPoint2 - (endPoint2 - center).Normalize() * num5;
			XYZ xyz6 = xyz - (xyz - center).Normalize() * num5;
			if (Geometry.IsEqual((xyz2 - endPoint).Normalize(), (endPoint - center).Normalize()))
			{
				xyz4 = endPoint + (endPoint - center).Normalize() * num5;
				xyz5 = endPoint2 + (endPoint2 - center).Normalize() * num5;
				xyz6 = xyz + (xyz - center).Normalize() * num5;
			}
			newCenterLine = Arc.Create(xyz4 + vtMove, xyz5 + vtMove, xyz6 + vtMove);
		}

		private bool HasFlipArcWallGroup(Curve kernelWallCenterCurve, ref List<Wall> walls)
		{
			Arc arc = (walls[0].Location as LocationCurve).Curve as Arc;
			XYZ endPoint = ((walls[walls.Count - 1].Location as LocationCurve).Curve as Arc).GetEndPoint(0);
			XYZ xyz = Geometry.CalculateFootPoint(arc, endPoint);
			XYZ pt = (endPoint - xyz).Normalize();
			Arc arc2 = kernelWallCenterCurve as Arc;
			XYZ xyz2 = (endPoint - arc2.Center).Normalize();
			if (Geometry.IsEqual(arc2.Normal, XYZ.BasisZ))
			{
				xyz2 = -xyz2;
			}
			if (Geometry.IsEqual(pt, xyz2))
			{
				walls.Reverse();
				return false;
			}
			return true;
		}

		private ExternalCommandData m_ComdData;

		private UIApplication m_UIApp;

		private UIDocument m_UIDoc;

        private Autodesk.Revit.ApplicationServices.Application m_App;

		private Document m_Doc;

		private double m_doffset = Common.MMToFeet(200.0);
	}
}
